
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "gpio.h"

#define GPIO_BASE_ADDR                            (0x120D0000)
#define GPIO_GRP(group_id)                        (g_pGpioBaseAddr+0x1000*group_id)
#define GPIO_DATA(group_reg_base, gpio_offset)    (((group_reg_base) + 0x000) + (1 << ((gpio_offset) + 2)))
#define GPIO_DIR(group_reg_base)                  ((group_reg_base) + 0x400)
#define GPIO_IS(group_reg_base)                   ((group_reg_base) + 0x404)
#define GPIO_IBE(group_reg_base)                  ((group_reg_base) + 0x408)
#define GPIO_IEV(group_reg_base)                  ((group_reg_base) + 0x40C)
#define GPIO_IE(group_reg_base)                   ((group_reg_base) + 0x410)
#define GPIO_RIS(group_reg_base)                  ((group_reg_base) + 0x414)
#define GPIO_MIS(group_reg_base)                  ((group_reg_base) + 0x418)
#define GPIO_IC(group_reg_base)                   ((group_reg_base) + 0x41C)

#define PAGE_SIZE_MASK (~(0xfff))
#define PAGE_SIZE 0x1000
#define GPIO_MAP_MEM_SIZE 0xC000

static const char dev[]="/dev/mem";
static HI_VOID *g_pGpioBaseAddr =GPIO_BASE_ADDR;// NULL;
static HI_BOOL g_bGpioInit = HI_FALSE;

HI_VOID * COMM_MMAP(HI_U32 u32RetAddr, HI_U32 u32MapMemSize)
{
	HI_S32 fd = open (dev, O_RDWR | O_SYNC);
	if (fd < 0)
	{
	    printf("open %s error!\n", dev);
	    return NULL;
	}
    
	/* addr align in page_size(4K) */
	unsigned long phy_addr_in_page;
	unsigned long page_diff;
	phy_addr_in_page = u32RetAddr & PAGE_SIZE_MASK;
	page_diff = u32RetAddr - phy_addr_in_page;

	/* size in page_size */
	unsigned long size_in_page;
	unsigned long size = u32MapMemSize;
	size_in_page =((size + page_diff - 1) & PAGE_SIZE_MASK) + PAGE_SIZE;

	void *addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);
	if (addr == MAP_FAILED)
	{
	    printf("mmap @ 0x%x error!\n", phy_addr_in_page);
	    close(fd);
	    return NULL;
	}

  close(fd);
  
	return addr+page_diff;
}

HI_VOID COMM_MUNMAP(HI_VOID *pRegAddr, HI_U32 u32MapMemSize)
{
	munmap(pRegAddr, u32MapMemSize);
}


HI_S32 HI_GPIOMNG_Init(PINMUX_REG_S *paPinmuxReg, HI_U8 u8PinmuxRegSize)
{
  HI_S32 s32Ret;
  HI_S32 i;
  // PINMUX
  for(i = 0; i < u8PinmuxRegSize; i++)
  {
    HI_VOID *pRegAddr = COMM_MMAP(paPinmuxReg[i].u32Reg, PAGE_SIZE);
    if(pRegAddr)
    {
      // 判断功能位, 这样可以保护以前已经复用过且配置好的管脚高低状态
      if((*((HI_U32 *)pRegAddr)&0x0f) != (paPinmuxReg[i].u32Val&0x0f))
      {
        *((HI_U32 *)pRegAddr) = paPinmuxReg[i].u32Val;
      }
      COMM_MUNMAP(pRegAddr, PAGE_SIZE);
    }
    else
    {
      printf("gpio pinmux failed!");
      return HI_FAILURE;
    }
  }

  // MAP GPIO_BASE
  g_pGpioBaseAddr = COMM_MMAP(GPIO_BASE_ADDR, GPIO_MAP_MEM_SIZE);
  if(g_pGpioBaseAddr == NULL)
  {
     printf("gpio map failed!");
     return HI_FAILURE;
  }

  g_bGpioInit = HI_TRUE;
  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_Denit()
{
  if(g_bGpioInit)
  {
    COMM_MUNMAP(g_pGpioBaseAddr, GPIO_MAP_MEM_SIZE);
    g_bGpioInit = HI_FALSE;
  }

  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_SetDir(HI_U8 u8Gpio, HI_U8 u8Dir)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDirAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDirAddr = GPIO_DIR(pGroupAddr);
  if(u8Dir)
  {
    *(HI_U32 *)pDirAddr |= (0x1<<u8GpioBit);
  }
  else
  {
    *(HI_U32 *)pDirAddr &= ~(0x1<<u8GpioBit);
  }

  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_GetDir(HI_U8 u8Gpio)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDirAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDirAddr = GPIO_DIR(pGroupAddr);
  if((*(HI_U32 *)pDirAddr)&(0x1<<u8GpioBit))
  {
    return 1;
  }
  else
  {
    return 0;
  }
}

HI_S32 HI_GPIOMNG_SetVal(HI_U8 u8Gpio, HI_U8 u8Val)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDataAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDataAddr = GPIO_DATA(pGroupAddr, u8GpioBit);
  if(u8Val)
  {
    *(HI_U32 *)pDataAddr |= (0x1<<u8GpioBit);
  }
  else
  {
    *(HI_U32 *)pDataAddr &= ~(0x1<<u8GpioBit);
  }

  return HI_SUCCESS;
}

HI_S32 HI_GPIOMNG_GetVal(HI_U8 u8Gpio)
{
  if(!g_bGpioInit) return HI_FAILURE;
  HI_U8 u8GpioGroup;
  HI_U8 u8GpioBit;
  HI_VOID *pGroupAddr;
  HI_VOID *pDataAddr;

  u8GpioGroup = u8Gpio/8;
  u8GpioBit = u8Gpio%8;
  pGroupAddr = GPIO_GRP(u8GpioGroup);
  pDataAddr = GPIO_DATA(pGroupAddr, u8GpioBit);
  if((*(HI_U32 *)pDataAddr)&(0x1<<(u8GpioBit+2)))
  {
    return 1;
  }
  else
  {
    return 0;
  }

  return HI_SUCCESS;
}

/*
example:gpio6_6

void gpio6_6_init(){    
     PINMUX_REG_S pinmux[1]={0x111f0024,0x0600};  //pin mux select
     HI_GPIOMNG_Init(pinmux,1);  
     HI_GPIOMNG_SetDir(6*8+6,1);  //gpio_group*8  + gpio_bit , gpio6_6:6*8+6
}

void gpio6_6_high(){
    HI_GPIOMNG_SetVal(6*8+6,1);   //gpio_group*8  + gpio_bit , gpio6_6:6*8+6; 1 :high ,0:low 
}
void gpio6_6_low(){
      HI_GPIOMNG_SetVal(6*8+6,0);
}
*/